/**
 * @brief 使用libcurl实现的文𢓐下载模块
 * @version 0.1
 * @date 2019-1-16
 * @author 宋炜
 */

#ifndef __DWLD_HPP__
#define __DWLD_HPP__
#include <string>
#include <fstream>
#include <atomic>
#include <functional>

#include <curl/curl.h>
#include "url.hpp"
#include "ary_str.hpp"

#define DEFAULT_HTTP_GET_BUFF_SIZE      (2024)

class Downloader{
public:
	enum err_code{
		ERR_CURL_CTX_NULL = 1000,               // LIBCURL 句柄指错误
		ERR_DATA_POINTER_NULL ,                 // 数据指针错误
		ERR_SYS_WRITE_FILE,                     // 写文𢓐错误
		ERR_UNSUPPORTED_PROTOCOL,               // 不支持的通讯协议
		ERR_FROM_FILE_EMPTY,                    // 源数据路径空
		ERR_TO_FILE_EMPTY,                      // 保存文𢓐路径空
		ERR_INIT_LIBCURL,
		ERR_FTP_PERFORM,
		ERR_HTTP_PERFORM,
		OK = 0
	};
private:
	CURL                    * p_ctx;        // libcurl 句柄
	std::atomic< bool >       m_dbg;        // 是否显示调试信息
	std::atomic< bool >       m_ssl;        // 
	std::string               m_caPath;     // 
	std::string               m_from;       // 来源URL
	std::string               m_to;         // 保存文𢓐路径

	std::ofstream             m_file;       // 文𢓐对象
	/**
	 * @brief 接收到数据处理完后的回调函数。这个函数目的是处理操作错误和调试用。函数原型：
	 *  void fun( err_code err , const char * data , size_t len );
	 * @param err 错误代码
	 * @param data, 接到的数据
	 * @param len , 接到的数据长度
	 */
	std::function< void ( err_code , const char * , size_t ) >  m_cb;
	/**
	 * @brief 完成后调用这个函数，是否操作完成由err参数指明，函数原型：
	 *   void fun( err_code err )
	 * @param err , 错误代码。如果下载成功，err = OK
	 */
	std::function< void ( err_code ) > cb_finish;
private:
	/**
	 * @brief 根据设置配置
	 */
	void enable_dbg_msg();
	void enable_ssl();
	void general_opts();
	/**
	 * @brief HTTP(S) get 通讯方式
	 * @param strUrl ， 目标URL
	 * @param buff ， 数据缓冲区
	 * @return 成功操作返回OK，否则返回错误代码
	 */
	err_code https_get( const std::string & Url, void *buff );
	/**
	 * @brief HTTP(S) POST 通讯方式
	 * @param Url , 目标URL
	 * @param data, 要提交的数据
	 * @param rst , 服务器返回数据
	 * @return ， 成功返回OK，否则返回错误代码
	 */
	err_code https_post( const std::string& Url, const std::string& data );
	/**
	 * @brief HTTP（S） GET 下载文𢓐
	 * @param url ， 文𢓐下载URL
	 * @param file , 要保存的文𢓐名称
	 * @return ， 成功返回OK，否则返回错误代码
	 */
	err_code https_get( const std::string& url , std::function< void ( err_code , const char * , size_t ) > fun );
	err_code https_get( const std::string & strUrl, const ArrayString& heads , std::function< void ( err_code , const char * , size_t ) > fun );
	/**
	 * @brief HTTP（S） POST 下载文𢓐
	 * @param url ， 文𢓐下载URL
	 * @param file , 要保存的文𢓐名称
	 * @return ， 成功返回OK，否则返回错误代码
	 */
	err_code https_post( const std::string& url , const ArrayString& headers , const std::string& data , const std::string& file );
	err_code https_post( const std::string& url , 
				const ArrayString& headers , 
				const std::string& data , 
				std::function< void ( err_code , const char * , size_t ) > fun );
	/**
	 * @brief FTP（S） 下载文𢓐
	 * @param url ， 文𢓐下载URL
	 * @param file , 要保存的文𢓐名称 
	 * @return ， 成功返回OK，否则返回错误代码
	 * @note url中应该包含用户名和密码
	 */
	err_code ftp( const URL& url , const std::string& tofile );
	err_code ftp( const URL& url , std::function< void ( err_code , const char * , size_t ) > fun );
	/**
	 * @brief 执行文𢓐下载
	 * @param from 源文𢓐
	 * @param to 目标文𢓐
	 * @return ， 成功返回OK，否则返回错误代码
	 */
	err_code download_file(const std::string & from , const std::string & to );
	/**
	 * @brief 调整libcurl设置
	 * @param url ， 源文𢓐URL
	 * @return ， 返回协议字符串
	 */
	const std::string prepare_protocol( const URL& url );
	/**
	 * @brief 准备文𢓐对象
	 * @param file , 文𢓐名称
	 * @return 成功返回true，否则返回false
	 */
	bool prepare_file( const std::string& file );
public:
	/**
	 * @brief 构造对象。黙认不采用SSH，无调试信息。
	 * @note 如果ssh设罱为false ， 但是url中的协议部分定义了SSH支持，则自动开启SSH。
	 */
	Downloader( );
	/** 
	 * @param ssh ， SSH开关
	 * @param dbg , 调试信息
	 */
	Downloader( bool ssh , bool dbg = false );
	/**
	 * @brief 构造
	 * @param url 来源文𢓐
	 * @param to 目标文𢓐
	 * @param ssl 是否开启SSL
	 * @param dbg 是否开启调试信息
	 */
	Downloader( const std::string& url , const std::string & to , bool ssl = false , bool dbg = false );
	virtual ~Downloader();
	/**
	 * @brief 设置下载完成回调函数
	 * @param fun , 回调函数
	 */
	void onFinish( std::function< void (err_code)> fun );
	/**
	 * @brief 开始执行下载。这个函数调用前要设定好文𢓐
	 * @return 成功返回true, 否则返回false
	 */
	void Run( );
	/**
	 * @brief 开始执行下载，这个函数按照参数重新调𢿄协议和文𢓐参数
	 * @param url 来源文𢓐
	 * @param to 目标文𢓐
	 * @return 成功返回true, 否则返回false
	 */
	void Run( const std::string& url , const std::string& to );
	/**
	 * @brief 处理接收到数据后的操作
	 * @param data
	 * @param len
	 */
	void OnRecv( const char * data , size_t len );
};

#endif
